import math
import numpy

import pygame2
import pygame2.sdl.constants as constants
import pygame2.sdl.event as event

import pygame2.sdl.video as video

import pygame2.sdlgfx.primitives as primitives
import pygame2.sdlgfx as sdlgfx

import pygame2.sdlttf as sdlttf
import pygame2.font as font

import pygame2.sdlmixer as sdlmixer
import pygame2.sdlmixer.constants as sdlmixer_constants

class Paddle(object):
    def __init__(self, surface):
        self.surface = surface
        self.x = 50
        self.y = 100
        self.width = 20
        self.height = 100

        self.x_velocity = 0
        self.y_velocity = 0

    def update(self):
        self.x += self.x_velocity
        self.y += self.y_velocity

    def draw(self):
        self.update()
        primitives.box(self.surface, pygame2.Rect (self.x, self.y, self.width, self.height), pygame2.Color(0, 255, 0))

class AIPaddle(Paddle):
    def __init__(self, surface, ball_list):
        super(AIPaddle, self).__init__(surface)
        self.x = self.surface.width - (50 + self.width)

        self.ball_list = ball_list

    def update(self):
        self.y_velocity = 0
        for ball in self.ball_list:
            if ball.x_velocity > 0:
                self.y_velocity = (self.y + self.height / 2 - ball.y) * -1
                self.y_velocity = math.copysign(1, self.y_velocity)

        super(AIPaddle, self).update()

class PongGame(object):
    def __init__(self):
        self.screen = video.set_mode(500, 500)
        self.drawing_surface = video.Surface(self.screen.width, self.screen.height)

        self.fps_manager = sdlgfx.FPSmanager(60)

        #self.font = sdlttf.sysfont.get_sys_font(font.get_families()[0], 10)
        self.font = sdlttf.sysfont.get_sys_font("dejavusans", 10)


        self.ball_list = []

        self.paddle = Paddle(self.drawing_surface)
        self.ai_paddle = AIPaddle(self.drawing_surface, self.ball_list)

        self.boop_sound = sdlmixer.Chunk(open("boop.wav"))
        self.channel = sdlmixer.Channel(1)

        self.run = True

        self.main_loop()

    def event_loop(self):
        for e in event.get ():
            if e.type == constants.QUIT:
                self.run = False

            if e.type == constants.KEYDOWN:
                if e.key == constants.K_ESCAPE:
                    self.run = False

            if e.type in (constants.KEYDOWN, constants.KEYUP):
                speed = 1 * e.type == constants.KEYDOWN
                if e.key == constants.K_UP:
                    self.paddle.y_velocity = -speed
                if e.key == constants.K_DOWN:
                    self.paddle.y_velocity = speed

            if e.type == constants.MOUSEBUTTONDOWN:
                ball = Paddle(self.drawing_surface)
                ball.x, ball.y = e.pos
                ball.width = ball.height = 20
                ball.x_velocity = ball.y_velocity = -1
                self.ball_list.append(ball)

    def collisions(self):
        for ball in self.ball_list:

            if ball.y <= 0:
                ball.y = 0
                ball.y_velocity *= -1
                self.channel.play(self.boop_sound)

            elif ball.y + ball.height >= self.screen.height:
                ball.y = self.screen.height - ball.height
                ball.y_velocity *= -1
                self.channel.play(self.boop_sound)

            if ball.x + ball.height >= self.screen.width:
                ball.x = self.screen.width - ball.height
                ball.x_velocity *= -1
                self.channel.play(self.boop_sound)

            elif ball.x <= 0:
                ball.x = 0
                ball.x_velocity *= -1
                self.channel.play(self.boop_sound)

            for paddle in (self.paddle, self.ai_paddle):
                if paddle.x + paddle.width >= ball.x and ball.x + ball.width >= paddle.x:
                    if paddle.y + paddle.height >= ball.y and ball.y + ball.height >= paddle.y:
                        ball.x_velocity *= -1
                        ball.y_velocity *= -1
                        self.channel.play(self.boop_sound)

    def render_scene(self):
        self.drawing_surface.fill(pygame2.Color(0, 0, 0))
        self.paddle.draw()
        self.ai_paddle.draw()

        for ball in self.ball_list:
            ball.draw()

    def render_hud(self):
        fps_text = "FPS: %.02f" % self.fps_manager.framerate
        fps_surface = self.font.render(fps_text, pygame2.Color(0, 255, 0))

        self.drawing_surface.blit(fps_surface, (2, 2))

    def render_effects(self):
        for y in range(0, self.drawing_surface.height, 4):
            primitives.line(self.drawing_surface, 0, y, self.drawing_surface.width, y, pygame2.Color(0, 0, 0))
            primitives.line(self.drawing_surface, 0, y + 1, self.drawing_surface.width, y + 1, pygame2.Color(0, 0, 0))

    def main_loop(self):
        while self.run:
            self.event_loop()

            self.collisions()

            self.render_scene()
            self.render_effects()
            self.render_hud()

            self.screen.blit(self.drawing_surface, (0, 0))

            self.fps_manager.delay()

            self.screen.flip()

def main():
    video.init()
    sdlttf.init()

    sdlmixer.init()
    sdlmixer.open_audio(44100, sdlmixer_constants.DEFAULT_FORMAT, 2, 2048)

    PongGame()

    video.quit()
    sdlttf.quit()

    sdlmixer.close_audio()
    sdlmixer.quit()

"""
def array_samples(sound, raw = True):
    """'Modified from the deprecated pygame2.sdlmixer.numpysndarray module'"""
    # Info is a (freq, format, stereo) tuple
    info = sdlmixer.query_spec()[1:]
    if not info:
        raise pygame2.Error ("Mixer not initialized")
    fmtbytes = (abs (info[1]) & 0xff) >> 3
    channels = info[2]
    if raw:
        data = sound.buf.raw
    else:
        data = sound.buf

    shape = (len (data) // fmtbytes,)
    if channels > 1:
        shape = (shape[0] // channels, channels)

    # mixer.init () does not support different formats from the ones below,
    # so MSB/LSB stuff is silently ignored.
    typecode = { sdlmixer_constants.AUDIO_U8 : numpy.uint8, # AUDIO_U8
                 sdlmixer_constants.AUDIO_U16SYS : numpy.uint16, # AUDIO_U16 / AUDIO_U16SYS
                 sdlmixer_constants.AUDIO_S8 : numpy.int8, # AUDIO_S8
                 sdlmixer_constants.AUDIO_S16SYS : numpy.int16  # AUDUI_S16 / AUDIO_S16SYS
                 }[info[1]]

    array = numpy.fromstring (data, typecode)
    array.shape = shape
    return array
    
def generate_sine(frequency, duration):
    l = []
    factor = 2 * math.pi * frequency
    for sample_index in range(int(sdlmixer.query_spec()[1] * duration)):
        sample = math.sin(sample_index * 128.83412014943490)
        l.append(int(sample * 26217.0))
    return l
    
def test():
    channel = sdlmixer.Channel(1)
    sound = sdlmixer.Chunk(open("boop.wav"))
    
    l = list()
    for s in array_samples(sound):
        l.append(s[0])

    print l
    print
    print generate_sine(200, 0.10252)

    print sdlmixer.query_spec()
    print len(l)
    print len(l) / 44100.0
    print max(l), min(l)

    print int(sdlmixer.query_spec()[1] * 0.10252)
"""


if __name__ == "__main__":
    main()
